package org.sean.framework.util;

import org.sean.framework.Constants;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.Query;
import javax.servlet.http.HttpServletRequest;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * Date: 2016/10/10 14:27
 *
 * @author Sean.xie
 */
public class IPUtil {

    // 1代表A类，2代表B类，3代表C类；4代表其它类型
    public static final int IP_A_TYPE = 1;
    public static final int IP_B_TYPE = 2;
    public static final int IP_C_TYPE = 3;
    public static final int IP_OTHER_TYPE = 4;
    /**
     * 需要解析的IP请求头名称
     */
    private static final Set<String> ORG_IP_HEADER_NAMES = new HashSet<>();
    private static final String IP_UNKNOWN = Constants.UNKNOWN;
    // IpV4的正则表达式，用于判断IpV4地址是否合法
    private static final String IPV4_REGEX = "((\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3})";
    // A类地址范围：1.0.0.1---126.255.255.254
    private static final int[] IP_A_TYPE_RANGE;
    // B类地址范围：128.0.0.1---191.255.255.254
    private static final int[] IP_B_TYPE_RANGE;
    // C类地址范围：192.168.0.0～192.168.255.255
    private static final int[] IP_C_TYPE_RANGE;
    // A,B,C类地址的默认mask
    private static final int DEFAULT_IP_A_MASK;
    private static final int DEFAULT_IP_B_MASK;
    private static final int DEFAULT_IP_C_MASK;
    private static String currentIp;

    // 初始化
    static {

        ORG_IP_HEADER_NAMES.add("X-Forwarded-For");
        ORG_IP_HEADER_NAMES.add("X-REAL-IP");
        ORG_IP_HEADER_NAMES.add("REAL-IP");
        ORG_IP_HEADER_NAMES.add("Proxy-Client-IP");
        ORG_IP_HEADER_NAMES.add("HTTP_CLIENT_IP");
        ORG_IP_HEADER_NAMES.add("HTTP_X_FORWARDED_FOR");
        ORG_IP_HEADER_NAMES.add("WL-Proxy-Client-IP");

        IP_A_TYPE_RANGE = new int[2];
        IP_A_TYPE_RANGE[0] = getIpV4Value("1.0.0.1");
        IP_A_TYPE_RANGE[1] = getIpV4Value("126.255.255.254");

        IP_B_TYPE_RANGE = new int[2];
        IP_B_TYPE_RANGE[0] = getIpV4Value("128.0.0.1");
        IP_B_TYPE_RANGE[1] = getIpV4Value("191.255.255.254");

        IP_C_TYPE_RANGE = new int[2];
        IP_C_TYPE_RANGE[0] = getIpV4Value("192.168.0.0");
        IP_C_TYPE_RANGE[1] = getIpV4Value("192.168.255.255");

        DEFAULT_IP_A_MASK = getIpV4Value("255.0.0.0");
        DEFAULT_IP_B_MASK = getIpV4Value("255.255.0.0");
        DEFAULT_IP_C_MASK = getIpV4Value("255.255.255.0");
    }

    // 系统子网掩码，它与ip组成一个地址
    private int mask;

    /**
     * 默认255.255.255.0
     */
    public IPUtil() {
        mask = getIpV4Value("255.255.255.0");
    }

    /**
     * @param masks 任意的如"255.255.254.0"等格式，如果格式不合法，抛出UnknownError异常错误
     */
    public IPUtil(String masks) {
        mask = getIpV4Value(masks);
        if (mask == 0) {
            throw new UnknownError();
        }
    }

    /**
     * 比较两个ip地址是否在同一个网段中，如果两个都是合法地址，两个都是非法地址时，可以正常比较；
     * 如果有其一不是合法地址则返回false；
     * 注意此处的ip地址指的是如“192.168.1.1”地址
     *
     * @param ip1  ip1
     * @param ip2  ip2
     * @param mask mask
     * @return 是否在同一网段
     */
    public static boolean checkSameSegment(String ip1, String ip2, int mask) {
        // 判断IPV4是否合法
        if (!ipV4Validate(ip1)) {
            return false;
        }
        if (!ipV4Validate(ip2)) {
            return false;
        }
        int ipValue1 = getIpV4Value(ip1);
        int ipValue2 = getIpV4Value(ip2);
        return (mask & ipValue1) == (mask & ipValue2);
    }

    /**
     * 比较两个ip地址是否在同一个网段中，如果两个都是合法地址，两个都是非法地址时，可以正常比较；
     * 如果有其一不是合法地址则返回false；
     * 注意此处的ip地址指的是如“192.168.1.1”地址
     *
     * @param ip1 ip1
     * @param ip2 ip2
     * @return 是否在同一网段
     */
    public static boolean checkSameSegmentByDefault(String ip1, String ip2) {
        // 获取默认的Mask
        int mask = getDefaultMaskValue(ip1);
        return checkSameSegment(ip1, ip2, mask);
    }

    /**
     * 获取ip值与mask值与的结果
     *
     * @param ip   ip
     * @param mask mask
     * @return 32bit值
     */
    public static int getSegmentValue(String ip, int mask) {
        int ipValue = getIpV4Value(ip);
        return (mask & ipValue);
    }

    /**
     * 判断ipV4或者mask地址是否合法，通过正则表达式方式进行判断
     *
     * @param ipv4 ip
     * @return 结果
     */
    public static boolean ipV4Validate(String ipv4) {
        return ipv4Validate(ipv4, IPV4_REGEX);
    }

    /**
     * 判断ip有效性
     *
     * @param addr  ip
     * @param regex 正则
     * @return 是否符合
     */
    private static boolean ipv4Validate(String addr, String regex) {
        if (addr == null) {
            return false;
        } else {
            return Pattern.matches(regex, addr.trim());
        }
    }

    /**
     * 比较两个ip地址，如果两个都是合法地址，则1代表ip1大于ip2，-1代表ip1小于ip2,0代表相等；
     * 如果有其一不是合法地址，如ip2不是合法地址，则ip1大于ip2，返回1，反之返回-1；两个都是非法地址时，则返回0；
     * 注意此处的ip地址指的是如“192.168.1.1”地址，并不包括mask
     *
     * @param ip1 ip1
     * @param ip2 ip2
     * @return 结果
     */
    public static int compareIpV4s(String ip1, String ip2) {
        int result = 0;
        int ipValue1 = getIpV4Value(ip1);     // 获取ip1的32bit值
        int ipValue2 = getIpV4Value(ip2); // 获取ip2的32bit值
        if (ipValue1 > ipValue2) {
            result = -1;
        } else if (ipValue1 <= ipValue2) {
            result = 1;
        }
        return result;
    }

    /**
     * 检测ipV4 的类型，包括A类，B类，C类，其它（C,D和广播）类等
     *
     * @param ipV4 ip v4
     * @return 返回1代表A类，返回2代表B类，返回3代表C类；返回4代表D类
     */
    public static int checkIpV4Type(String ipV4) {
        int inValue = getIpV4Value(ipV4);
        if (inValue >= IP_C_TYPE_RANGE[0] && inValue <= IP_C_TYPE_RANGE[1]) {
            return IP_C_TYPE;
        } else if (inValue >= IP_B_TYPE_RANGE[0] && inValue <= IP_B_TYPE_RANGE[1]) {
            return IP_B_TYPE;
        } else if (inValue >= IP_A_TYPE_RANGE[0] && inValue <= IP_A_TYPE_RANGE[1]) {
            return IP_A_TYPE;
        }
        return IP_OTHER_TYPE;
    }

    /**
     * 获取默认mask值，如果IpV4是A类地址，则返回{@linkplain #DEFAULT_IP_A_MASK}，
     * 如果IpV4是B类地址，则返回{@linkplain #DEFAULT_IP_B_MASK}，以此类推
     *
     * @param anyIpV4 任何合法的IpV4
     * @return mask 32bit值
     */
    public static int getDefaultMaskValue(String anyIpV4) {
        int checkIpType = checkIpV4Type(anyIpV4);
        int maskValue = 0;
        switch (checkIpType) {
            case IP_C_TYPE:
                maskValue = DEFAULT_IP_C_MASK;
                break;
            case IP_B_TYPE:
                maskValue = DEFAULT_IP_B_MASK;
                break;
            case IP_A_TYPE:
                maskValue = DEFAULT_IP_A_MASK;
                break;
            default:
                maskValue = DEFAULT_IP_C_MASK;
        }
        return maskValue;
    }

    /**
     * 获取默认mask地址，A类地址对应255.0.0.0，B类地址对应255.255.0.0，
     * C类及其它对应255.255.255.0
     *
     * @param ip ip
     * @return mask 字符串表示
     */
    public static String getDefaultMaskStr(String ip) {
        return trans2IpStr(getDefaultMaskValue(ip));
    }

    /**
     * 将ip 32bit值转换为如“192.168.0.1”等格式的字符串
     *
     * @param ipValue 32bit值
     * @return ip
     */
    public static String trans2IpStr(int ipValue) {
        // 保证每一位地址都是正整数
        return ((ipValue >> 24) & 0xff) + "." + ((ipValue >> 16) & 0xff) + "." + ((ipValue >> 8) & 0xff) + "." + (ipValue & 0xff);
    }

    /**
     * 将ip byte数组值转换为如“192.168.0.1”等格式的字符串
     *
     * @param ipBytes 32bit值
     * @return ip
     */
    public static String trans2IpV4Str(byte[] ipBytes) {
        // 保证每一位地址都是正整数
        return (ipBytes[0] & 0xff) + "." + (ipBytes[1] & 0xff) + "." + (ipBytes[2] & 0xff) + "." + (ipBytes[3] & 0xff);
    }

    /**
     * 取IP V4
     *
     * @param ipOrMask ip
     * @return ip
     */
    public static int getIpV4Value(String ipOrMask) {
        byte[] addr = getIpV4Bytes(ipOrMask);
        int address1 = addr[3] & 0xFF;
        address1 |= ((addr[2] << 8) & 0xFF00);
        address1 |= ((addr[1] << 16) & 0xFF0000);
        address1 |= ((addr[0] << 24) & 0xFF000000);
        return address1;
    }

    /**
     * 取IP V4
     *
     * @param ipOrMask ip
     * @return ip
     */
    public static byte[] getIpV4Bytes(String ipOrMask) {
        try {
            String[] addrs = ipOrMask.split("\\.");
            int length = addrs.length;
            byte[] addr = new byte[length];
            for (int index = 0; index < length; index++) {
                addr[index] = (byte) (Integer.parseInt(addrs[index]) & 0xff);
            }
            return addr;
        } catch (Exception e) {
            // do nothing
        }
        return new byte[4];
    }

    /**
     * 获取本机IP
     * 此方法耗时很长,系统初始化时加载
     *
     * @return ip
     */
    public static String getIP() {
        if (currentIp == null) {
            try {
                InetAddress ia = InetAddress.getLocalHost();
                currentIp = ia.getHostAddress();
            } catch (Exception e) {
                currentIp = "";
            }
        }
        return currentIp;
    }

    /**
     * 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址;
     *
     * @param request 请求
     * @return 原始IP
     */
    public static String getRemoteIp(HttpServletRequest request) {
        // 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址

        String ip = null;
        for (String name : ORG_IP_HEADER_NAMES) {
            ip = request.getHeader(name);
            if (!isEmptyIp(ip)) {
                break;
            }
        }

        if (isEmptyIp(ip)) {
            ip = request.getRemoteAddr();
        }

        if (isEmptyIp(ip)) {
            ip = IP_UNKNOWN;
        } else {
            if (ip.length() > 15) {
                String[] ips = ip.split(",");
                for (String strIp : ips) {
                    if (!(IP_UNKNOWN.equalsIgnoreCase(strIp))) {
                        ip = strIp;
                        break;
                    }
                }
            }
        }
        return ip;
    }

    private static boolean isEmptyIp(String ip) {
        return StringUtil.isEmpty(ip) || IP_UNKNOWN.equalsIgnoreCase(ip);
    }

    /**
     * 获取服务端口
     *
     * @return 服务端口
     */
    public static Integer getServerPort() {
        try {
            MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();
            Set<ObjectName> objectNames = beanServer.queryNames(new ObjectName("*:type=Connector,*"),
                    Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
            String port = objectNames.iterator().next().getKeyProperty("port");
            return Integer.valueOf(port);
        } catch (Exception e) {
            return null;
        }
    }

    public int getMask() {
        return mask;
    }

    /**
     * 比较两个ip地址是否在同一个网段中，如果两个都是合法地址，两个都是非法地址时，可以正常比较；
     * 如果有其一不是合法地址则返回false；
     * 注意此处的ip地址指的是如“192.168.1.1”地址，并不包括mask
     *
     * @param ip1 ip1
     * @param ip2 ip2
     * @return 是否同一网段
     */
    public boolean checkSameSegment(String ip1, String ip2) {
        return checkSameSegment(ip1, ip2, mask);
    }

    /**
     * 获取ip值与mask值与的结果
     *
     * @param ipV4 ip
     * @return 32bit值
     */
    public int getSegmentValue(String ipV4) {
        int ipValue = getIpV4Value(ipV4);
        return (mask & ipValue);
    }
}
